import numpy as np
import matplotlib
# 使用 WebAgg 后端以便浏览器交互预览
matplotlib.use('WebAgg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 固定 WebAgg 预览地址与端口，便于打开预览
plt.rcParams['webagg.address'] = '127.0.0.1'
plt.rcParams['webagg.port'] = 8765

def lcg(a, c, m, x0, num):
    """生成线性同余随机数序列"""
    sequence = [x0]
    for _ in range(num - 1):
        next_val = (a * sequence[-1] + c) % m
        sequence.append(next_val)
    return np.array(sequence)

# 初始化参数
a = 2396548189
c = 0
m = 2**31
x0 = 1
num_points = 20000  # 生成足够多的数，保证三维点的密度
sequence = lcg(a, c, m, x0, num_points)

# 每三个数一组，分别作为x、y、z坐标
x = sequence[:-2]
y = sequence[1:-1]
z = sequence[2:]

# 归一化到0-1区间
x_norm = x / m
y_norm = y / m
z_norm = z / m

fig = plt.figure(figsize=(8, 6), dpi=100)
ax = fig.add_subplot(111, projection='3d')
ax.mouse_init(rotate_btn=1, zoom_btn=3)  # 启用拖拽旋转与滚轮缩放

# 绘制散点图（优化性能：去除透明度与边缘）
sc = ax.scatter(x_norm, y_norm, z_norm, s=1, c='blue', alpha=1.0, edgecolors='none')

# 拖拽期间使用低采样数据以加速交互
sample_size = 5000
rng = np.random.default_rng(42)
idx = rng.choice(len(x_norm), size=min(sample_size, len(x_norm)), replace=False)
x_fast, y_fast, z_fast = x_norm[idx], y_norm[idx], z_norm[idx]

def on_press(event):
    # 左键按下触发快速模式
    if event.inaxes == ax and event.button == 1:
        sc._offsets3d = (x_fast, y_fast, z_fast)
        fig.canvas.draw_idle()

def on_release(event):
    # 左键释放恢复高精度数据
    if event.inaxes == ax and event.button == 1:
        sc._offsets3d = (x_norm, y_norm, z_norm)
        fig.canvas.draw_idle()

fig.canvas.mpl_connect('button_press_event', on_press)
fig.canvas.mpl_connect('button_release_event', on_release)

# 设置坐标轴范围
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_zlim(0, 1)

# 隐藏坐标轴标签（使图更简洁，与示例风格一致）
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_zticklabels([])

# 添加标题
ax.set_title(r'线性同余发生器 $x_n = 65539x_{n-1} \mathrm{mod} 2^{31}$ 的晶格结构')

plt.show()
